package org.spring.servlet;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.spring.mvcframework.annotation.GPAutowired;
import org.spring.mvcframework.annotation.GPConfiguration;
import org.spring.mvcframework.annotation.GPController;
import org.spring.mvcframework.annotation.GPRequestMapping;
import org.spring.mvcframework.annotation.GPRequestParam;
import org.spring.mvcframework.annotation.GPService;
import org.spring.mvcframework.annotation.GPValue;

public class DispatcherServlet extends HttpServlet {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private static final String LOCATION = "contextConfigLocation";

	private Properties p = new Properties();

	private List<String> classNames = new ArrayList<String>();

	private Map<String, Object> ioc = new HashMap<String, Object>();

	private Map<String, Object> handlerMapping = new HashMap<String, Object>();

	public DispatcherServlet() {
		super();
	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		this.doPost(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		try {
			doDispatch(req, resp);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void init(ServletConfig config) throws ServletException {

		doLoadConfig(config.getInitParameter(LOCATION));

		doScanner(p.getProperty("scanPackage"));

		doInstance();

		doConfig();

		doAutowired();

		initHandlerMapping();
	}

	private void doLoadConfig(String location) {
		InputStream fis = null;
		try {
			fis = this.getClass().getClassLoader().getResourceAsStream(location);
			p.load(fis);
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (fis != null) {
					fis.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 增加类
	 * 
	 * @param packageName
	 */
	private void doScanner(String packageName) {
		String classPath = this.getClass().getResource("/").getPath();
		String path = packageName.replaceAll("\\.", "/");
		// URL url = this.getClass().getClassLoader().getResource(classPath +
		// path);
		File dir = new File(classPath + path);
		for (File file : dir.listFiles()) {
			if (file.isDirectory()) {
				doScanner(packageName + "." + file.getName());
			} else {
				classNames.add(packageName + "." + file.getName().replace(".class", ""));
			}
		}
		System.out.println(classNames.toString());
	}

	/**
	 * 实现字符串首字符小写
	 * 
	 * @param str
	 * @return
	 */
	private String lowerFirstCase(String str) {
		char[] chars = str.toCharArray();
		chars[0] += 32;
		return String.valueOf(chars);
	}

	private void doInstance() {
		if (classNames.size() == 0) {
			return;
		} else {
			try {
				for (String className : classNames) {
					Class<?> clazz = Class.forName(className);
					if (clazz.isAnnotationPresent(GPController.class)) {
						String beanName = lowerFirstCase(clazz.getSimpleName());
						System.out.println(beanName);
						ioc.put(beanName, clazz.newInstance());
					} else if (clazz.isAnnotationPresent(GPService.class)) {
						GPService service = clazz.getAnnotation(GPService.class);
						String beanName = service.value();
						if (!"".equals(beanName.trim())) {
							ioc.put(beanName, clazz.newInstance());
							continue;
						}
						Class<?>[] interfaces = clazz.getInterfaces();
						if (interfaces != null && interfaces.length > 0) {
							for (Class<?> i : interfaces) {
								ioc.put(i.getName(), clazz.newInstance());
							}
						} else {
							ioc.put(clazz.getName(), clazz.newInstance());
						}
					} else if (clazz.isAnnotationPresent(GPConfiguration.class)) {
						ioc.put(clazz.getName(), clazz.newInstance());
					} else {
						continue;
					}

				}
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}
	}

	private void doConfig() {
		if (ioc.isEmpty()) {
			return;
		}
		for (Entry<String, Object> entry : ioc.entrySet()) {
			Field[] fields = entry.getValue().getClass().getDeclaredFields();
			for (Field field : fields) {
				if (!field.isAnnotationPresent(GPValue.class)) {
					continue;
				}
				GPValue autowired = field.getAnnotation(GPValue.class);
				String beanName = autowired.value().trim();
				if ("".equals(beanName)) {
					beanName = field.getType().getName();
				}
				field.setAccessible(true);
				try {
					field.set(entry.getValue(), p.getProperty(beanName));
				} catch (Exception e) {
					// TODO: handle exception
				}
			}
		}
	}

	private void doAutowired() {
		if (ioc.isEmpty()) {
			return;
		}
		for (Entry<String, Object> entry : ioc.entrySet()) {
			Field[] fields = entry.getValue().getClass().getDeclaredFields();
			for (Field field : fields) {
				if (!field.isAnnotationPresent(GPAutowired.class)) {
					continue;
				}
				GPAutowired autowired = field.getAnnotation(GPAutowired.class);

				String beanName = autowired.value().trim();
				if ("".equals(beanName)) {
					beanName = field.getType().getName();
				}
				field.setAccessible(true);
				try {
					field.set(entry.getValue(), ioc.get(beanName));
				} catch (Exception e) {
					// TODO: handle exception
				}
			}
		}
	}

	private void initHandlerMapping() {
		if (ioc.isEmpty()) {
			return;
		}

		for (Entry<String, Object> entry : ioc.entrySet()) {
			Class<?> clazz = entry.getValue().getClass();
			if (!clazz.isAnnotationPresent(GPController.class)) {
				continue;
			}
			String baseUrl = "";
			if (clazz.isAnnotationPresent(GPRequestMapping.class)) {
				GPRequestMapping requestMapping = clazz.getAnnotation(GPRequestMapping.class);
				baseUrl = requestMapping.value();
			}

			Method[] methods = clazz.getMethods();
			for (Method method : methods) {
				if (!method.isAnnotationPresent(GPRequestMapping.class)) {
					continue;
				}
				GPRequestMapping requestMapping = method.getAnnotation(GPRequestMapping.class);
				String url = ("/" + baseUrl + "/" + requestMapping.value()).replaceAll("/+", "/");
				handlerMapping.put(url, method);
				System.out.println("mapped" + url + "," + method);
			}
		}
	}

	private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
		if (this.handlerMapping.isEmpty()) {
			return;
		}

		String url = req.getRequestURI();
		String contextPath = req.getContextPath();

		url = url.replace(contextPath, "").replaceAll("/+", "/");
		if (!this.handlerMapping.containsKey(url)) {
			resp.getWriter().write("404 not Fount");
			return;
		}

		Map<String, String[]> params = req.getParameterMap();

		Method method = (Method) this.handlerMapping.get(url);

		Class<?>[] parameterTypes = method.getParameterTypes();
		Map<String, String[]> parameterMap = req.getParameterMap();
		// Enumeration<String> enumeString = req.getParameterNames();

		Object[] paramValues = new Object[parameterTypes.length];

		for (int i = 0; i < parameterTypes.length; i++) {
			Class parameterType = parameterTypes[i];
			if (parameterType == HttpServletRequest.class) {
				paramValues[i] = req;
				continue;
			} else if (parameterType == HttpServletResponse.class) {
				paramValues[i] = resp;
				continue;
			} else if (parameterType == String.class) {
				Annotation[][] an = method.getParameterAnnotations();
				for (int j = 0; j < an.length; j++) {
					for (Annotation an2 : an[j]) {
						GPRequestParam reqParam = (GPRequestParam) an2;
						if (reqParam.required()) {
							boolean falg = false;
							for (Entry<String, String[]> param : parameterMap.entrySet()) {
								if (reqParam.value() != null && reqParam.value().equals(param.getKey())) {
									System.out.println(param.getKey());
									String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "")
											.replaceAll("\\s", ",");
									paramValues[i] = value;
									falg = true;
								}
							}
							if(!falg)
							{
								System.out.println("参数名称不相同");
								throw new Exception();
							}
						}else{
							
						}

					}
				}

			}
		}

		String beanName = lowerFirstCase(method.getDeclaringClass().getSimpleName());
		method.invoke(this.ioc.get(beanName), paramValues);
	}

	public static void main(String[] args) {
		String str = "org.spring.demo";
		str = str.replaceAll("\\.", "/");

		// URL url = this.getClass().getClassLoader().getResource("/" +str);
		System.out.println(str);
	}

}
